import { FC, ReactElement, useCallback, useState, useEffect } from "react";
import styled, { keyframes } from "styled-components";
import { useSocketContext } from "../../api/context/SocketContext";
import { createConnect } from "../../api/socketHelper";
import { EMIT, EVENT, ALL_GAMERS, SOCKET } from "../../api/types";
import { Button, textEllipsis, TStatus } from "../../styled";
import { IFightData, IOpponent, IWaitFight } from "./types";

interface IProps {
  setFightData: React.Dispatch<React.SetStateAction<IFightData>>;
  setOpponent: React.Dispatch<React.SetStateAction<IOpponent>>;
}

const OnlinePlayers: FC<IProps> = ({
  setFightData,
  setOpponent,
}): ReactElement => {
  const [{ status, socket, nickname }, setSocketState] = useSocketContext();
  const [allGamers, setAllGamers] = useState<any>({});
  const [waitFight, setWaitFight] = useState<IWaitFight>({});

  useEffect(() => {
    //同步ALL_GAMERS变更
    localStorage.setItem(ALL_GAMERS, JSON.stringify(allGamers));
  }, [allGamers]);

  const handleSetSocketState = useCallback(
    (status) => setSocketState((prev: any) => ({ ...prev, ...status })),
    [setSocketState]
  );

  const listenNewGamer = useCallback(
    (socket: SOCKET) => {
      socket.on(EVENT.BROADCAST_NEW_GAMER, (newGame: any) => {
        //新玩家进入
        const { name, allGamers } = JSON.parse(newGame);
        //TODO toast 弹窗通知玩家进入
        console.log("广播: " + name + " 进入游戏");
        delete allGamers[nickname];
        setAllGamers(() => allGamers);
      });
    },
    [nickname]
  );

  const listenLeaveGame = useCallback((socket: SOCKET) => {
    socket.on(EVENT.BROADCAST_LEAVE_THE_GAME, (user: string) => {
      setAllGamers((prev: any) => {
        const allGamers = { ...prev };
        console.log(allGamers);
        delete allGamers[user];
        return allGamers;
      });
    });
  }, []);

  const handleAddWaitFight = useCallback(
    ({ nickname, socketId }: IWaitFight) => {
      setWaitFight((prev) => {
        const data = { ...prev };
        data[nickname] = socketId;
        return data;
      });
    },
    []
  );

  const handleRemoveWaitFight = useCallback((nickname) => {
    setWaitFight((prev) => {
      const data = { ...prev };
      delete data[nickname];
      return data;
    });
  }, []);

  const handleConnect = useCallback(() => {
    const socket = createConnect();
    console.log(socket);
    socket.on(EVENT.CONNECT, () => {
      console.log("connect success👏");
      //设置context
      handleSetSocketState({ status: true, socket });
      //告诉服务端爷来了
      socket.emit(EMIT.ENTER, nickname);
      //服务端回应当前在线的用户(不算自己)
      socket.on(EVENT.WELCOME, (allGamers) => {
        allGamers = JSON.parse(allGamers);
        console.log("allGamers:", allGamers);
        setAllGamers(allGamers);
      });
      //监听新玩家进入
      listenNewGamer(socket);
      //监听新玩家退出
      listenLeaveGame(socket);
      //接收 对战邀请
      socket.on(EVENT.FIGHT, (payload) => {
        const fight = JSON.parse(payload);
        setFightData((prev) => {
          const fightData = { ...prev };
          fightData[fight.nickname] = fight;
          return fightData;
        });
      });
      socket.on(EVENT.ACCEPTED_FIGHT, (payload: any) => {
        const opponent = JSON.parse(payload); // props: nickname socketId
        console.log("opponent:", opponent);
        setOpponent(() => opponent);
        //将 等待 变回 对战
        handleRemoveWaitFight(opponent.nickname);
      });
      //对战邀请被拒绝
      socket.on(EVENT.REJECTED_FIGHT, (payload) => {
        const { nickname } = JSON.parse(payload);
        handleRemoveWaitFight(nickname);
        console.log("对战被" + nickname + "拒绝");
      });

      // //TODO 接受了你的对战邀请
      // socket.on(EVENT.ACCEPT_FIGHT, (payload) => {});
    });
    socket.on(EVENT.ERROR, () => {
      alert("🥶出错了!");
      console.error("connect filed🥶");
    });
  }, [
    handleSetSocketState,
    nickname,
    listenNewGamer,
    listenLeaveGame,
    setFightData,
    setOpponent,
    handleRemoveWaitFight,
  ]);

  const handleDisConnect = useCallback(() => {
    socket.disconnect();
    handleSetSocketState({ status: false, socket: {} });
    setAllGamers(() => ({}));
  }, [socket, handleSetSocketState]);

  return (
    <Container>
      <p style={{ letterSpacing: "1px", marginBottom: "5px" }}>OnlinePlayers</p>
      <GamerList
        waitFight={waitFight}
        handleAddWaitFight={handleAddWaitFight}
        allGamers={allGamers}
        socket={socket}
        nickname={nickname}
      />
      <StatusButton
        nickname={nickname}
        handleDisConnect={handleDisConnect}
        handleConnect={handleConnect}
        status={status}
      />
    </Container>
  );
};

const GamerList = ({
  allGamers,
  socket,
  handleAddWaitFight,
  waitFight,
  nickname,
}: {
  allGamers: any;
  socket: SOCKET;
  handleAddWaitFight: (waitFight: IWaitFight) => void;
  waitFight: IWaitFight;
  nickname: string;
}) => {
  const handleToFight = useCallback(
    (target_nickname, socketId) => () => {
      const message = prompt("对战宣言:", "敢与我一战");
      if (message) {
        const payload = { target: socketId, message, nickname };
        //对战状态变成 等待中状态
        handleAddWaitFight({ nickname: target_nickname, socketId });
        //@ts-ignore
        socket.emit(EMIT.FIGHT_REQUEST, JSON.stringify(payload));
      }
    },
    [handleAddWaitFight, nickname, socket]
  );

  return (
    <div>
      <GamerItemStyled style={{ color: "#51f" }}>
        <strong style={{ width: "8rem", margin: "2px 0" }}>
          {nickname}(自己)
        </strong>
      </GamerItemStyled>
      {Object.entries(allGamers).map(([nickname, socketId]) => {
        return (
          <GamerItem
            handleToFight={handleToFight(nickname, socketId)}
            key={socketId as string}
            nickname={nickname}
            wait={!!waitFight[nickname]}
          />
        );
      })}
    </div>
  );
};

const GamerItem = ({
  nickname,
  handleToFight,
  wait,
}: {
  nickname: string;
  handleToFight: () => void;
  wait: boolean;
}) => {
  return (
    <GamerItemStyled>
      <strong>{nickname}</strong>
      <span
        style={wait ? { color: "#000" } : undefined}
        onClick={wait ? undefined : handleToFight}
      >
        <svg
          viewBox="0 0 1024 1024"
          version="1.1"
          xmlns="http://www.w3.org/2000/svg"
          p-id="2084"
          width="16"
          height="16"
        >
          <path
            d="M896 128.128l0.085333 150.314667-233.258666 233.216 120.661333 120.704 60.373333-60.330667 60.330667 60.330667-105.557333 105.6 120.661333 120.704-60.330667 60.330666-120.704-120.704-105.6 105.6-60.330666-60.330666 60.330666-60.373334L512 662.528l-120.661333 120.661333 60.373333 60.373334-60.330667 60.330666-105.6-105.6-120.704 120.704-60.330666-60.330666 120.746666-120.746667-105.642666-105.557333 60.330666-60.330667 60.330667 60.288 120.618667-120.661333-232.96-232.96L128 128l151.296 0.128L512 360.832 744.832 128 896 128.128zM300.8 692.650667l30.208 30.165333 120.618667-120.661333-30.165334-30.165334-120.661333 120.661334zM780.117333 213.376l-207.786666 207.744 30.122666 30.165333 208.256-208.170666v-29.738667h-30.592z m-566.741333 29.866667l479.616 479.616 30.165333-30.165334L243.882667 213.418667l-30.506667-0.042667v29.866667z"
            p-id="2085"
            fill="#20bd20"
          ></path>
        </svg>
        {wait ? "等待" : "对战"}
      </span>
    </GamerItemStyled>
  );
};

const StatusButton = ({
  status,
  handleConnect,
  handleDisConnect,
  nickname,
}: {
  status: boolean;
  nickname: string;
  handleConnect: () => void;
  handleDisConnect: () => void;
}) => {
  const handleOnClick = useCallback(() => {
    if (status) {
      const result = window.confirm("确定退出?");
      if (result) handleDisConnect();
    } else {
      if (nickname) handleConnect();
      else alert("请先输入有效昵称😁");
    }
  }, [handleConnect, handleDisConnect, nickname, status]);

  return (
    <Button
      style={{ margin: "2px 0" }}
      onClick={handleOnClick}
      width="100%"
      status={TStatus.PRIMARY}
    >
      {status ? "退出" : "进入房间"}
    </Button>
  );
};

export default OnlinePlayers;

const Container = styled.div`
  width: 10rem;
  height: 20rem;
  border-radius: 0 4px 4px 0;
  position: absolute;
  left: 0;
  top: 5rem;
  background: linear-gradient(145deg, #ffffff, #dde0dd);
  box-shadow: 22px 22px 86px #a7a9a7, -22px -22px 86px #ffffff;
  padding: 1rem;

  & > p {
    font-weight: bold;
  }

  @media (max-width: 875px) {
    position: relative;
    top: 0;
    width: 100%;
    height: 13rem;
  }
`;

const animateStyles = `
  0% {
      transform: rotate(0deg);
  }
  12%{
    transform: rotate(10deg);
  }
  25%{
    transform: rotate(-10deg);
  }
  50%{
    transform: rotate(10deg);
  }
  70%{
    transform: rotate(-10deg);
  }
  100%{
    transform: rotate(0deg);
  }
`;

const animate = keyframes`${animateStyles}`;

const GamerItemStyled = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-family: "Courier New", Courier, monospace;
  color: #20bd20;
  position: relative;

  & > strong {
    width: 5rem;
    ${textEllipsis}
  }
  & > span {
    display: flex;
    align-items: center;
    cursor: pointer;

    &:hover {
      & > svg {
        animation: ${animate} 1s linear;
      }
    }
  }
`;
